﻿Public NotInheritable Class Cell
    Implements IComparable

    Public Puzzle As Puzzle
    Public i, x, y, b As Integer
    Public f As String
    Public bg As Integer = 0
    Public Candidates As New List(Of Char)
    Public LDirections As New List(Of Cells)
    Public Neighbours, Sides As Cells
    Public Scale As Double = 1
    Public Groups As New Groups

    Sub New(ByVal Puzzle As Puzzle)
        Me.Puzzle = Puzzle
        Puzzle.NewCell(Me)
    End Sub

    Public Overrides Function ToString() As String
        Return "(" & x & "," & y & ")"
    End Function

    Dim m_g As String
    Property g(Optional ByVal UndoRedo As Boolean = False) As String
        Get
            Return m_g
        End Get
        Set(ByVal Value As String)
            If Value = g Then Exit Property
            If Not UndoRedo Then
                Puzzle.AddAction(New UndoAction(Me, g, Value))
            End If
            m_g = Value
            Invalidate()
        End Set
    End Property

    ReadOnly Property Given As Boolean
        Get
            Return Trim(g) > ""
        End Get
    End Property

    Sub Reset(ByVal Digits As String)
        f = ""
        Candidates = Digits.ToList
    End Sub

    ReadOnly Property CandidateStr() As String
        Get
            Return CStr(Candidates.ToArray)
        End Get
    End Property

    Default Public ReadOnly Property Contains(ByVal ch As String) As Boolean
        Get
            Return Candidates.Contains(ch)
        End Get
    End Property

    Sub SetValue(ByVal c As String)
        f = c
        If c <> "*" Then
            For Each Target In From p In Peers Where p(c)
                Target.Eliminate(c)
            Next
        End If
        Candidates.Clear()
        For Each Group In Groups
            Group.Eliminate(Me)
        Next
    End Sub

    ReadOnly Property Peers As Cells
        Get
            Return New Cells(From g In Groups From c In g.Cells Where Not c Is Me Select c)
        End Get
    End Property

    Sub Eliminate(ByVal ch As String)
        If Not Candidates.Contains(ch) Then Exit Sub
        Candidates.Remove(ch)
        For Each Group In Groups
            Group.Eliminate(Me, ch)
        Next
    End Sub

    Public Type As Integer = 1

    ReadOnly Property Index(ByVal d As Integer) As Integer
        Get
            Return Choose(d, x, y, b)
        End Get
    End Property

    ReadOnly Property Block() As List(Of PuzzleStudio.Cell)
        Get
            Return Groups.Where(Function(g) g.Type = 3)
        End Get
    End Property

    Sub Save(ByVal Doc As Xml.XmlTextWriter)
        Doc.WriteStartElement("Cell")
        Doc.WriteElementString("x", x)
        Doc.WriteElementString("y", y)
        Doc.WriteElementString("g", g)
        Doc.WriteEndElement()
    End Sub

#Region " Operators "

    Public Shared Operator &(ByVal Cell As Cell, ByVal Text As String) As String
        Return Cell.ToString & Text
    End Operator

    Public Shared Operator &(ByVal Text As String, ByVal Cell As Cell) As String
        Return Text & Cell.ToString
    End Operator

    Public Shared Operator *(ByVal Cell1 As Cell, ByVal Cell2 As Cell) As String
        Return CStr(Cell1.Candidates.Intersect(Cell2.Candidates).ToArray)
    End Operator

    Public Shared Operator >(ByVal Cell1 As Cell, ByVal Cell2 As Cell) As Boolean
        Return Cell1.i > Cell2.i
    End Operator

    Public Shared Operator <(ByVal Cell1 As Cell, ByVal Cell2 As Cell) As Boolean
        Return Cell1.i < Cell2.i
    End Operator

    'Public Shared Operator +(ByVal Cell1 As Cell, ByVal Cell2 As Cell) As String
    '    Return Cell1 + Cell2.Candidatestr
    'End Operator

    'Public Shared Operator +(ByVal Cell As Cell, ByVal Text As String) As String
    '    Dim Arr = Cell.Candidates.Union(Text.ToArray).ToArray
    '    Array.Sort(Arr)
    '    Return CStr(Arr)
    'End Operator

    Public Shared Operator -(ByVal Cell1 As Cell, ByVal Text As String) As String
        Return CStr(CStr(Cell1.CandidateStr).Except(Text).ToArray)
    End Operator

#End Region

    Dim m_Region As String
    Property Region As String
        Get
            If m_Region <> "" Then Return m_Region
            Dim Rg = (From r In Puzzle.Regions Where r.Cells.Contains(Me)).FirstOrDefault
            If Rg Is Nothing Then Return "."
            Return Puzzle.Regions.IndexOf(Rg)
        End Get
        Set(ByVal Value As String)
            m_Region = Value
        End Set
    End Property

    ReadOnly Property Min As Char
        Get
            If f <> "" Then Return f
            If Candidates.Count = 0 Then Return ""
            Return Candidates.Min
        End Get
    End Property

    ReadOnly Property Max As Char
        Get
            If f <> "" Then Return f
            If Candidates.Count = 0 Then Return ""
            Return Candidates.Max
        End Get
    End Property

    ReadOnly Property Rect As Rectangle
        Get
            Return Puzzle.CellRect(x, y)
        End Get
    End Property

    Public Sub New(ByVal Puzzle As Puzzle, ByVal x As Integer, ByVal y As Integer)
        Me.Puzzle = Puzzle
        Me.i = y * 100 + x
        Me.x = x
        Me.y = y
        g = " "
        f = ""
        Puzzle.NewCell(Me)
    End Sub

    Sub Invalidate()
        Puzzle.Invalidate(Puzzle.CellRect(x, y))
    End Sub

    Function DistanceFrom(ByVal Cell As Cell) As Integer
        Return Math.Abs(x - Cell.x) + Math.Abs(y - Cell.y)
    End Function

    Public Function CompareTo(ByVal obj As Object) As Integer Implements System.IComparable.CompareTo
        Return Math.Sign(i - obj.i)
    End Function

End Class
